
#ifndef CODE_H
#define CODE_H

#include "Item.h"
#include "Parser.h"
#include <string>
#include <iostream>

namespace mjc {

	class Code
	{
	public:
		//instructions
		static enum C
		{
			C_LOAD        =  1,
			C_LOAD0       =  2,
			C_LOAD1       =  3,
			C_LOAD2       =  4,
			C_LOAD3       =  5,
			C_STORE       =  6,
			C_STORE0      =  7,
			C_STORE1      =  8,
			C_STORE2      =  9,
			C_STORE3      = 10,
			C_GETSTATIC   = 11,
			C_PUTSTATIC   = 12,
			C_GETFIELD    = 13,
			C_PUTFIELD    = 14,
			C_CONST0      = 15,
			C_CONST1      = 16,
			C_CONST2      = 17,
			C_CONST3      = 18,
			C_CONST4      = 19,
			C_CONST5      = 20,
			C_CONST_M1    = 21,
			C_CONST       = 22,
			C_ADD         = 23,
			C_SUB         = 24,
			C_MUL         = 25,
			C_DIV         = 26,
			C_REM         = 27,
			C_NEG         = 28,
			C_SHL         = 29,
			C_SHR         = 30,
			C_NEW         = 31,
			C_NEWARRAY    = 32,
			C_ALOAD       = 33,
			C_ASTORE      = 34,
			C_BALOAD      = 35,
			C_BASTORE     = 36,
			C_ARRAYLENGTH = 37,
			C_POP         = 38,
			C_JMP         = 39,
			C_JEQ         = 40,
			C_JNE         = 41,
			C_JLT         = 42,
			C_JLE         = 43,
			C_JGT         = 44,
			C_JGE         = 45,
			C_CALL        = 46,
			C_RETURN      = 47,
			C_ENTER       = 48,
			C_EXIT        = 49,
			C_READ        = 50,
			C_PRINT       = 51,
			C_BREAD       = 52,
			C_BPRINT      = 53,
			C_TRAP		  = 54
		};

	private:
		static const unsigned int bufferSize = 8192;//fixed
		char buffer[bufferSize];
		int pc; //next free byte in buffer
		int mainPc; //pc of main function (set by parser)
		int dataSize; //length of static data in words (set by parser)

	public:
		Code(): pc(0), mainPc(-1), dataSize(0) {}
		Code(const Code& other);

		int Put(int c);//returns 0 when buffer fills completely
		int Put2(int c);
		int Put2(int c, int pos);
		int Put4(int c);
		unsigned char Get(int pos) { return buffer[pos]; }

		void Load(Item& item);//loads item on the expression stack
		void Assign(Item& item0, Item& item1);//creates an assignment expression item0 = item1
		void Assign(Item& item);

		void Jump(int address);//unconditional jump
		void FalseJump(Parser::Op op, int address);//jump if op is false
		void Fixup(int address);//patch jump target at address so that it jumps to the current pc


		int GetPc() { return pc; }
		int GetMainPc() { return mainPc; }
		void SetMainPc(int mainPc) { this->mainPc = mainPc; }
		int GetDatSize() { return dataSize; }
		void SetDataSize(int dataSize) { this->dataSize = dataSize; }

		void PrintDecoded() const;

		void Write(std::ostream& os);
	};
}

#endif